{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 版面图片分类器模型训练\n",
    "\n",
    "> 使用卷积神经网络模型进行版面图片分类，全套程序使用Python完成，主要的开发框架有Tensorflow、Keras、Numpy、Pillow。如需重新训练该模型，可在`'./datasets'`目录当中添加新的训练样本，然后点击菜单栏 Kernel -> Restart & Run All，完成训练后会自动将模型保存为`'cnn_clf.h5'`文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入深度学习框架并开始构建神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.random.seed(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/kk/.local/lib/python3.5/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import keras\n",
    "from keras.layers import (\n",
    "    Input, Conv2D, MaxPool2D, BatchNormalization,\n",
    "    Flatten, Dense, Dropout)\n",
    "from keras.models import Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import os, pickle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from libs.datasetFuncs import readDatasets, getDatas, getTargets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 初始配置\n",
    "\n",
    "- `mini_img_shape` 通常不需要修改；\n",
    "- `classes_num` 指定类别的数量；\n",
    "- `batch_size` 训练批次大小，一般不需要改变；\n",
    "- `epochs_num` 训练轮数，可以根据实际情况调整，但不宜过大。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "USE_CLEAN = False\n",
    "USE_TRAINING = False\n",
    "\n",
    "mini_img_shape = (64, 64, 3) # 图片（高，宽，rgb通道）\n",
    "batch_size = 128\n",
    "epochs_num = 60\n",
    "\n",
    "datasets_path = './datasets/'\n",
    "labels = os.listdir(datasets_path)\n",
    "classes_num = len(labels)\n",
    "model_filename = '../libs/lay_clf3.h5'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "input_img = Input(shape=mini_img_shape, name='input_img') # 定义输入"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象&方法说明\n",
    "\n",
    "- Conv2D 二维卷积操作\n",
    "- MaxPool2D 最大值下采样\n",
    "- BatchNormalization 批标准化，标准化层间输入数据数值\n",
    "- relu 为激活方程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TensorShape([Dimension(None), Dimension(6), Dimension(6), Dimension(20)])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conv1 = Conv2D(1, (3, 3), activation='relu')(input_img)\n",
    "pool1 = MaxPool2D((2, 2))(conv1)\n",
    "conv2 = Conv2D(10, (3, 3), activation='relu')(pool1)\n",
    "pool2 = MaxPool2D((2, 2))(conv2)\n",
    "\n",
    "normalized = BatchNormalization(axis=1)(pool2)\n",
    "conv3 = Conv2D(20, (3, 3), activation='relu')(normalized)\n",
    "pool3 = MaxPool2D((2, 2))(conv3)\n",
    "pool3.get_shape()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象&方法说明\n",
    "\n",
    "- Flatten 用于将卷积后的值展开，方便全连接层的矩阵积操作\n",
    "- Dense 全连接层\n",
    "- softmax 为激活方程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TensorShape([Dimension(None), Dimension(5)])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flattened = Flatten()(pool3)\n",
    "dense1 = Dense(128, activation='relu')(flattened)\n",
    "dropout = Dropout(0.5)(dense1)\n",
    "dense2 = Dense(classes_num, activation='softmax', name='dense2')(dropout)\n",
    "dense2.get_shape()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if not USE_CLEAN and os.path.isfile(model_filename):\n",
    "    model = keras.models.load_model(model_filename)\n",
    "else:\n",
    "    USE_CLEAN = True\n",
    "    model = Model(inputs=input_img, outputs=dense2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象&方法说明\n",
    "\n",
    "- rmsprop 训练优化器的一种\n",
    "- categorical_crossentropy 多分类交叉商，常用于多分类模型训练\n",
    "- metrics=['accuracy'] 测试模型准确度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if USE_CLEAN:\n",
    "    model.compile(optimizer='rmsprop', \n",
    "                  loss='categorical_crossentropy',\n",
    "                  metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 可视化神经网络结构\n",
    "\n",
    "**PS:** 使用该功能需安装 pydot 包，可以使用命令：`pip install pydot` 进行安装。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<svg height=\"968pt\" viewBox=\"0.00 0.00 441.00 968.00\" width=\"441pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g class=\"graph\" id=\"graph0\" transform=\"scale(1 1) rotate(0) translate(4 964)\">\n",
       "<title>G</title>\n",
       "<polygon fill=\"white\" points=\"-4,4 -4,-964 437,-964 437,4 -4,4\" stroke=\"none\"/>\n",
       "<!-- 140000872738432 -->\n",
       "<g class=\"node\" id=\"node1\"><title>140000872738432</title>\n",
       "<polygon fill=\"none\" points=\"72.5,-913.5 72.5,-959.5 360.5,-959.5 360.5,-913.5 72.5,-913.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"133.5\" y=\"-932.8\">InputX: InputLayer</text>\n",
       "<polyline fill=\"none\" points=\"194.5,-913.5 194.5,-959.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222\" y=\"-944.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"194.5,-936.5 249.5,-936.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222\" y=\"-921.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"249.5,-913.5 249.5,-959.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305\" y=\"-944.3\">(None, 64, 64, 3)</text>\n",
       "<polyline fill=\"none\" points=\"249.5,-936.5 360.5,-936.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305\" y=\"-921.3\">(None, 64, 64, 3)</text>\n",
       "</g>\n",
       "<!-- 140000872738544 -->\n",
       "<g class=\"node\" id=\"node2\"><title>140000872738544</title>\n",
       "<polygon fill=\"none\" points=\"72,-830.5 72,-876.5 361,-876.5 361,-830.5 72,-830.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"133.5\" y=\"-849.8\">conv2d_1: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"195,-830.5 195,-876.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222.5\" y=\"-861.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"195,-853.5 250,-853.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222.5\" y=\"-838.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"250,-830.5 250,-876.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-861.3\">(None, 64, 64, 3)</text>\n",
       "<polyline fill=\"none\" points=\"250,-853.5 361,-853.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-838.3\">(None, 62, 62, 1)</text>\n",
       "</g>\n",
       "<!-- 140000872738432&#45;&gt;140000872738544 -->\n",
       "<g class=\"edge\" id=\"edge1\"><title>140000872738432-&gt;140000872738544</title>\n",
       "<path d=\"M216.5,-913.366C216.5,-905.152 216.5,-895.658 216.5,-886.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-886.607 216.5,-876.607 213,-886.607 220,-886.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872738600 -->\n",
       "<g class=\"node\" id=\"node3\"><title>140000872738600</title>\n",
       "<polygon fill=\"none\" points=\"30,-747.5 30,-793.5 403,-793.5 403,-747.5 30,-747.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"133.5\" y=\"-766.8\">max_pooling2d_1: MaxPooling2D</text>\n",
       "<polyline fill=\"none\" points=\"237,-747.5 237,-793.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"264.5\" y=\"-778.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"237,-770.5 292,-770.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"264.5\" y=\"-755.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"292,-747.5 292,-793.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347.5\" y=\"-778.3\">(None, 62, 62, 1)</text>\n",
       "<polyline fill=\"none\" points=\"292,-770.5 403,-770.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347.5\" y=\"-755.3\">(None, 31, 31, 1)</text>\n",
       "</g>\n",
       "<!-- 140000872738544&#45;&gt;140000872738600 -->\n",
       "<g class=\"edge\" id=\"edge2\"><title>140000872738544-&gt;140000872738600</title>\n",
       "<path d=\"M216.5,-830.366C216.5,-822.152 216.5,-812.658 216.5,-803.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-803.607 216.5,-793.607 213,-803.607 220,-803.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872252248 -->\n",
       "<g class=\"node\" id=\"node4\"><title>140000872252248</title>\n",
       "<polygon fill=\"none\" points=\"68.5,-664.5 68.5,-710.5 364.5,-710.5 364.5,-664.5 68.5,-664.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-683.8\">conv2d_2: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-664.5 191.5,-710.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-695.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-687.5 246.5,-687.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-672.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-664.5 246.5,-710.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-695.3\">(None, 31, 31, 1)</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-687.5 364.5,-687.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-672.3\">(None, 29, 29, 10)</text>\n",
       "</g>\n",
       "<!-- 140000872738600&#45;&gt;140000872252248 -->\n",
       "<g class=\"edge\" id=\"edge3\"><title>140000872738600-&gt;140000872252248</title>\n",
       "<path d=\"M216.5,-747.366C216.5,-739.152 216.5,-729.658 216.5,-720.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-720.607 216.5,-710.607 213,-720.607 220,-720.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872252808 -->\n",
       "<g class=\"node\" id=\"node5\"><title>140000872252808</title>\n",
       "<polygon fill=\"none\" points=\"26.5,-581.5 26.5,-627.5 406.5,-627.5 406.5,-581.5 26.5,-581.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-600.8\">max_pooling2d_2: MaxPooling2D</text>\n",
       "<polyline fill=\"none\" points=\"233.5,-581.5 233.5,-627.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"261\" y=\"-612.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"233.5,-604.5 288.5,-604.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"261\" y=\"-589.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"288.5,-581.5 288.5,-627.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347.5\" y=\"-612.3\">(None, 29, 29, 10)</text>\n",
       "<polyline fill=\"none\" points=\"288.5,-604.5 406.5,-604.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347.5\" y=\"-589.3\">(None, 14, 14, 10)</text>\n",
       "</g>\n",
       "<!-- 140000872252248&#45;&gt;140000872252808 -->\n",
       "<g class=\"edge\" id=\"edge4\"><title>140000872252248-&gt;140000872252808</title>\n",
       "<path d=\"M216.5,-664.366C216.5,-656.152 216.5,-646.658 216.5,-637.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-637.607 216.5,-627.607 213,-637.607 220,-637.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872253032 -->\n",
       "<g class=\"node\" id=\"node6\"><title>140000872253032</title>\n",
       "<polygon fill=\"none\" points=\"-2.84217e-14,-498.5 -2.84217e-14,-544.5 433,-544.5 433,-498.5 -2.84217e-14,-498.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-517.8\">batch_normalization_1: BatchNormalization</text>\n",
       "<polyline fill=\"none\" points=\"260,-498.5 260,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"287.5\" y=\"-529.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"260,-521.5 315,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"287.5\" y=\"-506.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"315,-498.5 315,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"374\" y=\"-529.3\">(None, 14, 14, 10)</text>\n",
       "<polyline fill=\"none\" points=\"315,-521.5 433,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"374\" y=\"-506.3\">(None, 14, 14, 10)</text>\n",
       "</g>\n",
       "<!-- 140000872252808&#45;&gt;140000872253032 -->\n",
       "<g class=\"edge\" id=\"edge5\"><title>140000872252808-&gt;140000872253032</title>\n",
       "<path d=\"M216.5,-581.366C216.5,-573.152 216.5,-563.658 216.5,-554.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-554.607 216.5,-544.607 213,-554.607 220,-554.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872253312 -->\n",
       "<g class=\"node\" id=\"node7\"><title>140000872253312</title>\n",
       "<polygon fill=\"none\" points=\"68.5,-415.5 68.5,-461.5 364.5,-461.5 364.5,-415.5 68.5,-415.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-434.8\">conv2d_3: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-415.5 191.5,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-446.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-438.5 246.5,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-423.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-415.5 246.5,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-446.3\">(None, 14, 14, 10)</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-438.5 364.5,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-423.3\">(None, 12, 12, 20)</text>\n",
       "</g>\n",
       "<!-- 140000872253032&#45;&gt;140000872253312 -->\n",
       "<g class=\"edge\" id=\"edge6\"><title>140000872253032-&gt;140000872253312</title>\n",
       "<path d=\"M216.5,-498.366C216.5,-490.152 216.5,-480.658 216.5,-471.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-471.607 216.5,-461.607 213,-471.607 220,-471.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872253648 -->\n",
       "<g class=\"node\" id=\"node8\"><title>140000872253648</title>\n",
       "<polygon fill=\"none\" points=\"26.5,-332.5 26.5,-378.5 406.5,-378.5 406.5,-332.5 26.5,-332.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-351.8\">max_pooling2d_3: MaxPooling2D</text>\n",
       "<polyline fill=\"none\" points=\"233.5,-332.5 233.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"261\" y=\"-363.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"233.5,-355.5 288.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"261\" y=\"-340.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"288.5,-332.5 288.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347.5\" y=\"-363.3\">(None, 12, 12, 20)</text>\n",
       "<polyline fill=\"none\" points=\"288.5,-355.5 406.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347.5\" y=\"-340.3\">(None, 6, 6, 20)</text>\n",
       "</g>\n",
       "<!-- 140000872253312&#45;&gt;140000872253648 -->\n",
       "<g class=\"edge\" id=\"edge7\"><title>140000872253312-&gt;140000872253648</title>\n",
       "<path d=\"M216.5,-415.366C216.5,-407.152 216.5,-397.658 216.5,-388.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-388.607 216.5,-378.607 213,-388.607 220,-388.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872253872 -->\n",
       "<g class=\"node\" id=\"node9\"><title>140000872253872</title>\n",
       "<polygon fill=\"none\" points=\"82,-249.5 82,-295.5 351,-295.5 351,-249.5 82,-249.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"137\" y=\"-268.8\">flatten_1: Flatten</text>\n",
       "<polyline fill=\"none\" points=\"192,-249.5 192,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219.5\" y=\"-280.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"192,-272.5 247,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219.5\" y=\"-257.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"247,-249.5 247,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"299\" y=\"-280.3\">(None, 6, 6, 20)</text>\n",
       "<polyline fill=\"none\" points=\"247,-272.5 351,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"299\" y=\"-257.3\">(None, 720)</text>\n",
       "</g>\n",
       "<!-- 140000872253648&#45;&gt;140000872253872 -->\n",
       "<g class=\"edge\" id=\"edge8\"><title>140000872253648-&gt;140000872253872</title>\n",
       "<path d=\"M216.5,-332.366C216.5,-324.152 216.5,-314.658 216.5,-305.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-305.607 216.5,-295.607 213,-305.607 220,-305.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872253984 -->\n",
       "<g class=\"node\" id=\"node10\"><title>140000872253984</title>\n",
       "<polygon fill=\"none\" points=\"96.5,-166.5 96.5,-212.5 336.5,-212.5 336.5,-166.5 96.5,-166.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"147.5\" y=\"-185.8\">dense_1: Dense</text>\n",
       "<polyline fill=\"none\" points=\"198.5,-166.5 198.5,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"226\" y=\"-197.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"198.5,-189.5 253.5,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"226\" y=\"-174.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"253.5,-166.5 253.5,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"295\" y=\"-197.3\">(None, 720)</text>\n",
       "<polyline fill=\"none\" points=\"253.5,-189.5 336.5,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"295\" y=\"-174.3\">(None, 128)</text>\n",
       "</g>\n",
       "<!-- 140000872253872&#45;&gt;140000872253984 -->\n",
       "<g class=\"edge\" id=\"edge9\"><title>140000872253872-&gt;140000872253984</title>\n",
       "<path d=\"M216.5,-249.366C216.5,-241.152 216.5,-231.658 216.5,-222.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-222.607 216.5,-212.607 213,-222.607 220,-222.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872254320 -->\n",
       "<g class=\"node\" id=\"node11\"><title>140000872254320</title>\n",
       "<polygon fill=\"none\" points=\"85,-83.5 85,-129.5 348,-129.5 348,-83.5 85,-83.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"147.5\" y=\"-102.8\">dropout_1: Dropout</text>\n",
       "<polyline fill=\"none\" points=\"210,-83.5 210,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"237.5\" y=\"-114.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"210,-106.5 265,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"237.5\" y=\"-91.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"265,-83.5 265,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"306.5\" y=\"-114.3\">(None, 128)</text>\n",
       "<polyline fill=\"none\" points=\"265,-106.5 348,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"306.5\" y=\"-91.3\">(None, 128)</text>\n",
       "</g>\n",
       "<!-- 140000872253984&#45;&gt;140000872254320 -->\n",
       "<g class=\"edge\" id=\"edge10\"><title>140000872253984-&gt;140000872254320</title>\n",
       "<path d=\"M216.5,-166.366C216.5,-158.152 216.5,-148.658 216.5,-139.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-139.607 216.5,-129.607 213,-139.607 220,-139.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140000872254376 -->\n",
       "<g class=\"node\" id=\"node12\"><title>140000872254376</title>\n",
       "<polygon fill=\"none\" points=\"95,-0.5 95,-46.5 338,-46.5 338,-0.5 95,-0.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"147.5\" y=\"-19.8\">OutputY: Dense</text>\n",
       "<polyline fill=\"none\" points=\"200,-0.5 200,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"227.5\" y=\"-31.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"200,-23.5 255,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"227.5\" y=\"-8.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"255,-0.5 255,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"296.5\" y=\"-31.3\">(None, 128)</text>\n",
       "<polyline fill=\"none\" points=\"255,-23.5 338,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"296.5\" y=\"-8.3\">(None, 5)</text>\n",
       "</g>\n",
       "<!-- 140000872254320&#45;&gt;140000872254376 -->\n",
       "<g class=\"edge\" id=\"edge11\"><title>140000872254320-&gt;140000872254376</title>\n",
       "<path d=\"M216.5,-83.3664C216.5,-75.1516 216.5,-65.6579 216.5,-56.7252\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"220,-56.6068 216.5,-46.6068 213,-56.6069 220,-56.6068\" stroke=\"black\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.SVG object>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from keras.utils.vis_utils import model_to_dot\n",
    "from IPython.display import SVG\n",
    "\n",
    "SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 测试模型效率，默认测试运行1000次所用时间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# def testPredict(epochs=1000):\n",
    "#     for i in range(epochs):\n",
    "#         test = np.random.random((1, 64, 64, 3)).astype('float32')\n",
    "#         model.predict(test)\n",
    "# %time testPredict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# x = np.random.random((1, 64, 64, 3)).astype('float32')\n",
    "# y = model.predict(x)\n",
    "# y.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开始导入数据\n",
    "\n",
    "**如果数据量庞大，可能需要比较长的时间导入，需要耐心等待！**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Class: 0: 100%|█████████████████████████████| 168/168 [00:00<00:00, 2721.02it/s]\n",
      "Class: 1: 100%|█████████████████████████████| 466/466 [00:00<00:00, 3890.39it/s]\n",
      "Class: 2: 100%|█████████████████████████████| 905/905 [00:00<00:00, 3470.82it/s]\n",
      "Class: 3: 100%|███████████████████████████| 2029/2029 [00:00<00:00, 2907.67it/s]\n",
      "Class: 4: 100%|███████████████████████████| 1081/1081 [00:00<00:00, 3179.35it/s]\n"
     ]
    }
   ],
   "source": [
    "datasets = readDatasets(labels)\n",
    "datas = getDatas(datasets, dtype='float32')\n",
    "targets = getTargets(datasets)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "train_X = (1 - datas / 255).reshape(-1, 64, 64, 3)\n",
    "train_Y = keras.utils.to_categorical(targets, num_classes=classes_num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开始训练神经网络模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if USE_TRAINING:\n",
    "    model.fit(train_X, train_Y, batch_size=batch_size, epochs=epochs_num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 保存训练好的模型\n",
    "\n",
    "**PS：**可能需要安装 h5py 库，可以通过命令：`pip install h5py` 进行安装。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if USE_TRAINING:\n",
    "    model.save(model_filename)\n",
    "    with open('labels.pkl', 'wb') as f:\n",
    "        pickle.dump(labels, f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 验证人工神经网络在训练集的效果\n",
    "\n",
    "其返回结果为[代价值，准确率]，**如果数据量足够，最好使用测试集进行训练！**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if os.path.isfile(model_filename):\n",
    "    model = keras.models.load_model(model_filename)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4649/4649 [==============================] - 5s 1ms/step\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[0.29966142189433165, 0.8055495805549581]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.evaluate(train_X, train_Y, batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 返回错误样本信息\n",
    "\n",
    "此处的“错误样本”指模型判断失误的样本。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error Count: 904\n"
     ]
    }
   ],
   "source": [
    "predict_Y = model.predict(train_X, batch_size=batch_size)\n",
    "\n",
    "predict_targets = (predict_Y == predict_Y.max(axis=1).reshape(-1, 1)).dot(np.arange(classes_num))\n",
    "errors_id = np.where(targets != predict_targets)[0].tolist()\n",
    "print('Error Count: {}'.format(len(errors_id)))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
